package com.hoshino.momochi.controller;


import com.hoshino.momochi.annotation.IgnoreLogin;
import com.hoshino.momochi.annotation.LimitCount;
import com.hoshino.momochi.annotation.MaintainToken;
import com.hoshino.momochi.model.DTO.UserDTO;
import com.hoshino.momochi.model.domain.User;
import com.hoshino.momochi.model.domain.UserDetails;
import com.hoshino.momochi.service.*;
import com.hoshino.momochi.util.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@RestController
@Slf4j
public class UserController {

    @Autowired
    private UserService userService;

    @Autowired
    private RedisService redisService;

    @Autowired
    private FileService fileService;

    @Autowired
    private SseService sseService;

    @Autowired
    private UserDetailsService userDetailsService;

    @Value("${url-static}")
    private String STATICURL;

    private final String USER_ID = CodeEnum.USERID.getMessage();

    private final String TOKEN_NAME = CodeEnum.TOKEN.getMessage();

    public static final String phone2 = "^[1][3-8][0-9]{9}$";

    public static boolean checkPhone(String phone) {

        Pattern pattern = Pattern.compile(phone2);
        Matcher matcher = pattern.matcher(phone);
        if (matcher.matches()) {
            return true;
        }
        return false;
    }


    /**
     * 注册
     *
     * @param userDTO username password acName(可以为空，默认为username) mail(注册邮箱) code(邮箱验证码)
     * @return
     */
    @RequestMapping("api/user/register")
    @IgnoreLogin
    public JsonResponse registerUser(@RequestBody UserDTO userDTO) {
        String username = userDTO.getUsername();
        String password = userDTO.getPassword();
        String acName = userDTO.getAc_name();
        String mail = userDTO.getMail();
        String code = userDTO.getCode();

        if (!StringUtils.isNotBlank(mail)) {
            // 邮箱为空
            return JsonResponse.success(CodeEnum.MAILNULL.getCode(), CodeEnum.MAILNULL.getMessage());
        }

        // 校验邮箱是否已经被注册
        Long checkMail = userDetailsService.checkMailIsNull(mail);
        if (Objects.nonNull(checkMail)) {
            return JsonResponse.success(CodeEnum.MAILEXIST.getCode(), CodeEnum.MAILEXIST.getMessage());
        }

        // 校验用户名是否正常输入
        if (!StringUtils.isNotBlank(username)) {
            return JsonResponse.success(CodeEnum.LOGINNULL.getCode(), CodeEnum.LOGINNULL.getMessage());
        }
        // 校验是否存在该用户名
        Number count = redisService.CheckUsername(username);
        if (count != null && count.intValue() != 0) {
            // 用户名被使用过
            return JsonResponse.success(CodeEnum.USERNAMEEXIST.getCode(), CodeEnum.USERNAMEEXIST.getMessage());
        }
        // 不存在该用户名 可以注册
        if (!StringUtils.isNotBlank(password)) {
            // 密码为空
            return JsonResponse.success(CodeEnum.LOGINNULL.getCode(), CodeEnum.LOGINNULL.getMessage());
        }
        if (!StringUtils.isNotBlank(code)) {
            // 验证码为空
            return JsonResponse.success(CodeEnum.CODENULL.getCode(), CodeEnum.CODENULL.getMessage());
        }

        String checkCode = redisService.getMailCode(mail); // 获取正确验证码
        if (!code.equals(checkCode)) {
            // 验证码错误
            return JsonResponse.success(CodeEnum.CODEFAIL.getCode(), CodeEnum.CODEFAIL.getMessage());
        }

        password = SHA256.getSHA256StrJava(password);
        User user = new User(username, password);
        userService.registerUser(user);
        // 用户详细信息创建
        UserDetails userDetails = new UserDetails();
        // 没有ac_name 则默认为username
        acName = StringUtils.isNotBlank(acName) ? acName : username;
        userDetails.setAcName(acName);
        userDetails.setUrl(STATICURL);
        userDetails.setCreateTime(new Date());
        userDetails.setUserId(user.getId());
        userDetails.setMail(mail);

        userDetailsService.addUserDetails(userDetails);
        redisService.AddUsername(username);

        return JsonResponse.success(CodeEnum.REGISTER.getCode(), CodeEnum.REGISTER.getMessage());
    }

    /**
     * ajax json
     * 登录
     * 返回 User，包含用户详细信息以及关注和被关注者部分信息，以及token字段
     *
     * @param request
     * @return
     */
    @RequestMapping("api/user/login")
    @LimitCount(key = "login", name = "登录接口", prefix = "limit")
    @IgnoreLogin
    @MaintainToken
    public JsonResponse loginUser(HttpServletRequest request, @RequestBody UserDTO userDTO) {
        String username = userDTO.getUsername();
        String password = userDTO.getPassword();
        if (!StringUtils.isNotBlank(username) || !StringUtils.isNotBlank(password)) {
            return JsonResponse.success(CodeEnum.LOGINNULL.getCode(), CodeEnum.LOGINNULL.getMessage());
        }
        password = SHA256.getSHA256StrJava(password);
        // 用户名和密码都非空
        User user = userService.getUserByUserName(username);
        // user 非空 那么 password 非空
        if (Objects.isNull(user) || !user.getPassword().equals(password)) {
            return JsonResponse.success(CodeEnum.ERROR.getCode(), CodeEnum.ERROR.getMessage());
        }
        // 密码正确
        Long uid = user.getId();
        // 生成相应token
        String token = TokenUtil.genAccessToken(username);
        request.setAttribute(TOKEN_NAME, token);
        request.setAttribute(USER_ID, uid);

        // 登录成功 更新 last_login_ip and last_login_time
        String ip = IPUtil.getIpAddr(request);
        UserDetails userDetails = new UserDetails();
        userDetails.setUserId(uid);
        userDetails.setLastLoginIp(ip);
        userDetails.setLastLoginTime(new Date());
        userDetailsService.updateUserIp(userDetails);

        // User res = userService.getFocusAndUserDetails(uid);
        // getFocusAndUserDetails 获取 uid 用户的 详细信息 UserDetails 关注列表 粉丝列表
        // 因此 可以 进行并行任务 将其拆分成 三个 任务 执行 加快 返回结果速度 （因为此时token已经生成 用户此时已经算登录状态，只是等待数据返回）
        User res = new User(null, null);
        res.setToken(token);
        try {
            res.setUserDetails(userDetailsService.getUserDetails(uid).get()); // 任务 1
            res.setLocalDetails(userDetailsService.getLocalUserDetails(uid).get());// 任务2 获取 我关注的人的信息
            res.setFocusDetails(userDetailsService.getFocusUserDetails(uid).get());// 任务 3 获取 关注我的人的信息
        } catch (Exception e) {
            // 异常执行操作
            log.info("获取用户信息错误{}", e.getMessage());
        }
        // After
        // 将 生成的token 保存至redis 并 设置过期时间为 latetime
        return JsonResponse.success(res, CodeEnum.SUCCESS.getMessage());
    }

    /**
     * 注销用户
     * Redis 相应 token-id  ttl <= 20min 可能还会存在
     * 如果直接拿着token 去请求其他，拦截器无法拦截 因为拦截器只会检查 Redis中是否存在 对应的 token
     * 那么此时 如果 token 存在 则 删除
     *
     * @param request
     * @return
     */
    @RequestMapping("api/user/revoke")
    public JsonResponse revokeUser(HttpServletRequest request) {
        Long uid = (Long) request.getAttribute(USER_ID);
        userService.updateById(uid); // 删除 user 中 用户信息
        userDetailsService.delUserDetails(uid); // 删除 user_details 中 用户信息
        String token = request.getHeader(TOKEN_NAME);// 删除登录状态
        redisService.RevokeUsername(token);
        sseService.closeConnect(uid); // 删除Sse连接
        return JsonResponse.success(CodeEnum.REVOKE.getCode(), CodeEnum.REVOKE.getMessage());
    }

    /**
     * 登出
     *
     * @param request
     * @return
     */
    @RequestMapping("api/user/loginOut")
    public JsonResponse loginOutUser(HttpServletRequest request) {
        Long uid = (Long) request.getAttribute(USER_ID);
        String token = request.getHeader(TOKEN_NAME);
        redisService.DelUserLoginSta(token);
        sseService.closeConnect(uid); // 删除sse连接
        return JsonResponse.success(CodeEnum.LOGINOUT.getCode(), CodeEnum.LOGINOUT.getMessage());
    }

    /**
     * 修改用户密码
     *
     * @param request
     * @param userDTO 需求字段 password code验证码
     * @return
     */
    @RequestMapping("api/user/reset/password")
    public JsonResponse resetPassword(HttpServletRequest request, @RequestBody UserDTO userDTO) {
        Long uid = (Long) request.getAttribute(USER_ID);
        String token = request.getHeader(TOKEN_NAME);
        String password = userDTO.getPassword();
        String mail = userService.getMailByUserId(uid);
        String code = userDTO.getCode();
        if (Objects.isNull(password) || Objects.isNull(mail) || Objects.isNull(code) || !StringUtils.isNotBlank(password) || !StringUtils.isNotBlank(mail) || !StringUtils.isNotBlank(code)) {
            // 密码 或 邮箱 或 验证码 为空
            return JsonResponse.success(CodeEnum.NOTNULL.getCode(), CodeEnum.NOTNULL.getMessage());
        }
        String checkCode = redisService.getMailCode(mail);
        if (!code.equals(checkCode)) {
            return JsonResponse.success(CodeEnum.CODEFAIL.getCode(), CodeEnum.CODEFAIL.getMessage());
        }
        password = SHA256.getSHA256StrJava(password);
        userService.updatePassword(password, uid); // 修改密码
        redisService.DelUserLoginSta(token); // 删除 登录状态
        sseService.closeConnect(uid); // 断开sse连接
        return JsonResponse.success(CodeEnum.LOGINOUT.getCode(), CodeEnum.LOGINOUT.getMessage());
    }

    /**
     * 忘记密码 找回
     *
     * @param userDTO password(new) mail code
     * @return
     */
    @RequestMapping("api/user/forget")
    @IgnoreLogin
    public JsonResponse forgetPassword(@RequestBody UserDTO userDTO) {
        String password = userDTO.getPassword();
        String mail = userDTO.getMail();
        String code = userDTO.getCode();
        if (Objects.isNull(password) || Objects.isNull(mail) || Objects.isNull(code)) {
            return JsonResponse.success(CodeEnum.NOTNULL.getCode(), CodeEnum.NOTNULL.getMessage());
        }
        String checkCode = redisService.getMailCode(mail);
        if (!code.equals(checkCode)) {
            return JsonResponse.success(CodeEnum.CODEFAIL.getCode(), CodeEnum.CODEFAIL.getMessage());
        }
        Long uid = userDetailsService.checkMailIsNull(mail);
        if (Objects.isNull(uid)) {
            //用户不存在
            return JsonResponse.success(CodeEnum.NOTEXIST.getCode(), CodeEnum.NOTEXIST.getMessage());
        }
        userService.updatePassword(SHA256.getSHA256StrJava(password), uid);
        return JsonResponse.success(CodeEnum.SUCCESS.getCode(), CodeEnum.SUCCESS.getMessage());
    }

    /**
     * 修改 mail
     *
     * @param request
     * @param userDTO 需求字段new_mail code验证码 new_code
     * @return
     */
    @RequestMapping("api/user/reset/mail")
    public JsonResponse resetAcName(HttpServletRequest request, @RequestBody UserDTO userDTO) {
        Long uid = (Long) request.getAttribute(USER_ID);
        String mail = userService.getMailByUserId(uid);
        String newMail = userDTO.getNew_mail();
        String code = userDTO.getCode();
        String newCode = userDTO.getNew_code();
        if (!StringUtils.isNotBlank(newMail)) {
            // 新邮箱空
            return JsonResponse.success(CodeEnum.MAILNULL.getCode(), CodeEnum.MAILNULL.getMessage());
        }
        if (Objects.nonNull(userDetailsService.checkMailIsNull(newMail))) {
            // 邮箱已经被 使用
            return JsonResponse.success(CodeEnum.MAILEXIST.getCode(), CodeEnum.MAILEXIST.getMessage());
        }
        if (!StringUtils.isNotBlank(mail) || !StringUtils.isNotBlank(code) || !StringUtils.isNotBlank(newCode)) {
            // 旧邮箱 或 旧邮箱验证码 或 新邮箱验证码 为空
            return JsonResponse.success(CodeEnum.ERROR.getCode(), CodeEnum.ERROR.getMessage());
        }
        String checkCode = redisService.getMailCode(mail);
        if (!code.equals(checkCode)) {
            return JsonResponse.success(CodeEnum.CODEFAIL.getCode(), CodeEnum.CODEFAIL.getMessage());
        }
        checkCode = redisService.getMailCode(newMail);
        if (!newCode.equals(checkCode)) {
            return JsonResponse.success(CodeEnum.CODEFAIL.getCode(), CodeEnum.CODEFAIL.getMessage());
        }
        UserDetails userDetails = new UserDetails();
        userDetails.setUserId(uid);
        userDetails.setMail(newMail);
        userDetailsService.updateMail(userDetails);
        return JsonResponse.success(newMail, CodeEnum.SUCCESS.getMessage());
    }

    /**
     * 更新用户信息 ac_name person_signature，tel，birthday
     *
     * @param request
     * @param userDTO
     * @return
     */
    @RequestMapping("api/user/reset/newDetails")
    public JsonResponse resetNewDetails(HttpServletRequest request, @RequestBody UserDTO userDTO) {
        Long uid = (Long) request.getAttribute(USER_ID);
        String personSignature = userDTO.getPerson_signature();
        String tel = userDTO.getTel();
        String acName = userDTO.getAc_name();
        String inBirthday = userDTO.getBirthday();
        if (!StringUtils.isNotBlank(tel) || !StringUtils.isNotBlank(acName) || !StringUtils.isNotBlank(inBirthday)) {
            return JsonResponse.success(CodeEnum.NOTNULL.getCode(), CodeEnum.NOTNULL.getMessage());
        }
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        Date birthday = new Date();
        try {
            birthday = sdf.parse(inBirthday);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        if (Objects.nonNull(tel) && !checkPhone(tel)) {
            return JsonResponse.success(CodeEnum.ERROR.getCode(), CodeEnum.ERROR.getMessage());
        }
        UserDetails userDetails = new UserDetails();
        userDetails.setUserId(uid);
        userDetails.setPersonSignature(personSignature);
        userDetails.setTel(AESUtil.encrypt(tel, AESUtil.appKey)); // 加密 存储手机号
        userDetails.setAcName(acName);
        userDetails.setBirthday(birthday);
        userDetailsService.updateUserDetailsNew(userDetails);
        userDetails = userDetailsService.getUserDetailsNAsync(uid);
        if (Optional.ofNullable(userDetails).isPresent()) {
            if (Objects.nonNull(userDetails.getTel())) {
                userDetails.setTel(new StringBuilder(tel).replace(3, 7, "****").toString());
            }
            return JsonResponse.success(userDetails, CodeEnum.SUCCESS.getMessage());
        }
        return JsonResponse.success(CodeEnum.ERROR.getCode(), CodeEnum.ERROR.getMessage());
    }

    /**
     * 上传文件 头像修改
     *
     * @param request
     * @param file
     * @return
     */
    @RequestMapping("api/user/upload")
    public JsonResponse uploadPng(HttpServletRequest request, MultipartFile file) {
        Long uid = (Long) request.getAttribute(USER_ID);
        String url = null;
        try {
            Map<String, String> map = fileService.upload(file);
            url = map.get("url");
            if (!StringUtils.isNotBlank(url)) {
                return JsonResponse.success(CodeEnum.ERROR.getCode(), CodeEnum.ERROR.getMessage());
            }
            UserDetails userDetails = new UserDetails();
            userDetails.setUserId(uid);
            userDetails.setUrl(url);
            userDetailsService.updateUrl(userDetails);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return JsonResponse.success(url, CodeEnum.SUCCESS.getMessage());
    }

    /**
     * (不使用，已经调整User字段，合并进登录，登录成功会直接返回用户详细的所有信息)
     * 获取当前登录 用户 的 所有 信息 包括 关注者和粉丝 的 详细信息列表
     *
     * @param request
     * @return
     */
    @RequestMapping("api/user/getAllDetails")
    public JsonResponse getAllDetails(HttpServletRequest request) {
        Long uid = (Long) request.getAttribute(USER_ID);
        User user = userService.getFocusAndUserDetails(uid);
        return JsonResponse.success(user, CodeEnum.SUCCESS.getMessage());
    }

    /**
     * 指定 user_id 获取 详细信息
     *
     * @param userDTO
     * @return
     */
    @RequestMapping("api/user/UserDetailsById")
    @IgnoreLogin
    public JsonResponse getUserDetailsByUserId(@RequestBody UserDTO userDTO) {
        Long user_id = userDTO.getUser_id();
        UserDetails userDetails = Optional.ofNullable(user_id).map(uid -> userService.getUserDetailsByUserId(uid)).orElse(null);
        if (!Objects.isNull(userDetails)) {
            // 有该用户则正常返回
            String tel = Objects.nonNull(userDetails.getTel()) && StringUtils.isNotBlank(userDetails.getTel()) ? AESUtil.decrypt(userDetails.getTel(), AESUtil.appKey) : null; // 解密
            if (Objects.nonNull(tel)) {
                userDetails.setTel(new StringBuilder(tel).replace(3, 7, "****").toString()); // 脱敏
            }
            return JsonResponse.success(userDetails, CodeEnum.SUCCESS.getMessage());
        }
        if (Optional.ofNullable(user_id).map(uid -> userService.getUserIdByIdAndNoIsDelete(uid)).isPresent()) {
            return JsonResponse.success(CodeEnum.USERREVOKE.getCode(), CodeEnum.USERREVOKE.getMessage());
        }
        return JsonResponse.success(CodeEnum.ERROR.getCode(), CodeEnum.ERROR.getMessage());
    }

    /**
     * 检验登录
     *
     * @return
     */
    @RequestMapping("api/checkLogin")
    public JsonResponse checkLoginSta() {
        return JsonResponse.success(1);
    }
}
